# -*- coding: UTF-8 -*-
__version__ = "v1.3"
__author__  = "Bifidy CAPs"

import yaml
import json
import datetime
import pymysql

__doc__ = """
# Release Note

1.3 2020-03-31
1. Add tablename in config, the key is `sql_table`
2. add rich text field in section element: *rich*
3. rich text will execute `rich_text.replace("\\n", "")`

1.2 2019-11-19
db.escape(content_json)

1.1 Update Config

1.0 Course Script Description

1. Loading YAML
Course.load()
 -> Chapter()
   -> Lesson().loadSection()
     -> Section()

2. Convert to JSON
Section.json

3. Create SQL
Course().SQL
Chapter().SQL
Lesson().SQL
Section().SQL

4. Write DB
Course().writeDB()
  -> Chapter().writeDB()
    -> Lesson().writeDB()
      -> Section().writeDB()
"""

# Utils

def log(msg):
    print(msg)

class InfoError(Exception):
    pass

# Content Class

class Course:
    def __init__(self, yaml_path):
        self.yaml_path = yaml_path

    def writeDB(self):
        sql = self.SQL
        if sql:
            log("[DB] Course Writing")
            log("execute:\n%s" % sql)
            cursor = db.cursor()
            cursor.execute(sql)
            db.commit()
            if "id" not in self.info:
                self.info["id"] = cursor.lastrowid
            cursor.close()
        for chapter in self.chapters:
            chapter.writeDB()
        log("[DB] Course writing completed")

    @property
    def config(self):
        log("Loading config")
        config = self.info.get("config")
        sql_host = config["sql_host"]
        sql_user = config["sql_user"]
        sql_pwd  = config["sql_pwd"]
        sql_table = config["sql_table"]
        only_update = config.get("only_update", False)
        log("Config-SQL: host|%s \t user|%s \t pwd|%s" %(sql_host, sql_user, sql_pwd))
        if only_update: 
            log("Config-Mode: only update when needed")  
        else: 
            log("Config-Mode: update all")
        return (sql_host, sql_user, sql_pwd, sql_table, only_update)

    def load(self):
        log("Loading yaml: %s" % self.yaml_path)
        with open(self.yaml_path, "r", encoding='utf-8') as f:
            self.info = yaml.load(f)
            f.close()
        log("Loading chapters")
        self.chapters = [Chapter(info=info, index=index, course_id=self.ID) for index, info in enumerate(self.info["chapters"])]
        log("Loading lessons")
        for lesson_info in self.info["lessons"]:
            c_i = lesson_info.get("chapter")
            if c_i is None:
                raise InfoError("can't found chpater in lesson\n%s" % lesson_info)
            chapter = self.chapters[c_i]
            index = len(chapter.lessons)
            lesson = Lesson(info=lesson_info, course_id=self.ID, chapter_id=chapter.ID, index=index)
            chapter.lessons.append(lesson)
        log("Loading yaml successful")
        return self
    
    def ID(self):
        course_id = self.info.get("id")
        if course_id:
            return course_id
        else:
            # SQL query
            return "__course_id__"

    @property
    def SQL(self):
        if only_update and not self.info.get("need_update"):
            return None

        title = self.info.get("course")
        if not title:
            raise InfoError("%s: course title not found" % self.yaml_path)

        overview    = self.info.get("overview", "")
        cover_url   = self.info.get("cover_url", "")
        course_id   = self.info.get("id")
        created_at  = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")

        if not course_id:
            sql = """/* Course: %s */
            INSERT INTO course (title, overview, cover_url, created_at) VALUES ("%s", "%s", "%s", "%s");
                  """ % (title, title, overview, cover_url, created_at)
        else:
            sql = """ /* Course: %s with id: %d*/
            UPDATE course SET title="%s", overview="%s", cover_url="%s", updated_at="%s" WHERE id=%d;
                  """ % (title, course_id, title, overview, cover_url, created_at, course_id)
        return sql

class Chapter:
    def __init__(self, info, index, course_id):
        self.info = info
        self.index = index
        self.course_id = course_id
        self.lessons = []

    def writeDB(self):
        sql = self.SQL
        if sql:
            log("[DB] Chapter %d Writing" % self.index)
            log("execute:\n%s" % sql)
            cursor = db.cursor()
            cursor.execute(sql)
            db.commit()
            if "id" not in self.info:
                self.info["id"] = cursor.lastrowid
            cursor.close()
        for lesson in self.lessons:
            lesson.writeDB()

    @property
    def SQL(self):
        if only_update and not self.info.get("need_update"):
            return None

        title = self.info.get("title")
        if not title:
            raise InfoError("Chapter title not found \n %s" % self.info)

        description = self.info.get("description", "")
        course_id   = str(self.course_id())
        # index 会用于展示
        index = self.index + 1

        chapter_id   = self.info.get("id")
        if chapter_id is None:
            sql = """/* Chapter: %s */
            INSERT INTO chapter (title, description, course_id, `index`) VALUES ("%s", "%s", %s, %d);
                  """ % (title, title, description, course_id, index)
        else:
            sql = """ /* Chapter: %s with id: %d*/
            UPDATE chapter SET title="%s", description="%s" WHERE id=%d;
                  """ % (title, chapter_id, title , description, chapter_id)
        return sql
    
    def ID(self):
        chapter_id = self.info.get("id")
        if chapter_id:
            return chapter_id
        else:
            # SQL query by course_id , index
            return "__chapter_id_%d__" % self.index

class Lesson:
    def __init__(self, info, course_id, chapter_id, index):
        self.info = info
        self.course_id = course_id
        self.chapter_id = chapter_id
        self.index_in_chapter = index
        self.load_section()
    
    def load_section(self):
        self.sections = []
        for index, section_info in enumerate(self.info["sections"]):
            section = Section(info=section_info, course_id=self.course_id, lesson_id=self.ID, index=index)
            self.sections.append(section)

    def writeDB(self):
        sql = self.SQL
        if sql:
            log("[DB] Lesson %d in Chapter %s Writing" % (self.index_in_chapter, str(self.chapter_id())))
            log("execute:\n%s" % sql)
            cursor = db.cursor()
            cursor.execute(sql)
            db.commit()
            if "id" not in self.info:
                self.info["id"] = cursor.lastrowid
            cursor.close()
        for section in self.sections:
            section.writeDB()

    @property
    def SQL(self):
        if only_update and not self.info.get("need_update"):
            return None

        title = self.info.get("title")
        if not title:
            raise InfoError("Lesson title not found \n %s" % self.info)

        description = self.info.get("description", "")
        video_url = self.info.get("video_url", "")

        course_id = str(self.course_id())
        chapter_id = str(self.chapter_id())
        # index_in_chapter 会用于展示
        index_in_chapter = self.index_in_chapter + 1
        created_at  = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")

        lesson_id = self.info.get("id")
        if lesson_id is None:
            sql = """/* Lesson: %s */
            INSERT INTO lesson (title, description, video_url, course_id, chapter_id, index_in_chapter, created_at) VALUES ("%s", "%s", "%s", %s, %s, %d, "%s");
                  """ % (title, title, description, video_url, course_id, chapter_id, index_in_chapter, created_at)
        else:
            sql = """ /* Lesson: %s with id: %d*/
            UPDATE lesson SET title="%s", description="%s", video_url="%s", updated_at="%s" WHERE id=%d;
                  """ % (title, lesson_id, title , description, video_url, created_at, lesson_id)
        return sql
    
    def ID(self):
        lesson_id = self.info.get("id")
        if lesson_id:
            return lesson_id
        else:
            # SQL query by chapter_id, index
            return "%s__lesson_id_%d__" % (self.chapter_id(), self.index_in_chapter)
    
class Section:
    
    def __init__(self,info, course_id, lesson_id, index):
        self.raw = info
        self.course_id = course_id
        self.lesson_id = lesson_id
        self.index_in_lesson = index

    def writeDB(self):
        sql = self.SQL
        if sql:
            log("[DB] Lesson Section %d in Lesson %s Writing" % (self.index_in_lesson, str(self.lesson_id())))
            log("execute:\n%s" % sql)
            cursor = db.cursor()
            cursor.execute(sql)
            db.commit()
            cursor.close()

    @property
    def SQL(self):
        if only_update and not self.raw.get("need_update"):
            return None

        content_json = self.json
        content_json = db.escape(content_json)

        course_id = str(self.course_id())
        lesson_id = str(self.lesson_id())
        index_in_lesson = self.index_in_lesson + 1
        created_at  = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")

        lesson_section_id = self.raw.get("id")
        if lesson_section_id is None:
            sql = """ INSERT INTO lesson_section (content_json, course_id, lesson_id, index_in_lesson, created_at) VALUES (%s, %s, %s, %d, "%s");
                  """ % (content_json, course_id, lesson_id, index_in_lesson, created_at)
        else:
            sql = """ UPDATE lesson_section SET content_json=%s, updated_at="%s" WHERE id=%d;
                  """ % (content_json, created_at, lesson_section_id)
        return sql

    @property
    def json(self):
        title = self.raw.get("title")
        if title is None:
            raise InfoError("Can't found title in Section\n%s", title)
        result = dict(title=title, content=self.content)
        return json.dumps(result, ensure_ascii=False,  separators=(',', ':'))

    @property
    def content(self):
        raw_content = self.raw["content"]
        if not isinstance(raw_content, list):
            return []
        content = []
        for element in raw_content:  
            result = SectionElement(element).result
            if result is not None:
                content.append(result)
        return content

class SectionElement:

    def __init__(self, element):
        self.raw = element
        self.value = None

    @property
    def result(self):
        if self.raw is None:
            raise InfoError("Element empty: %s", self.raw)

        if isinstance(self.raw, str):
            self.raw = dict(text=self.raw)

        if isinstance(self.raw, dict):
            if "text" in self.raw:
                return self.text
            elif "rich" in self.raw:
                return self.rich
            elif "image" in self.raw:
                return self.image
            elif "code" in self.raw:
                return self.code
            else:
                raise InfoError("Unknown element: %s", self.raw)

    @property
    def rich(self):
        raw_html = self.raw["rich"].replace("\n", "")
        result = dict(type="rich", text=raw_html)
        return result

    @property
    def text(self):
        result = dict(type="text", text=self.raw["text"])
        return result

    @property
    def image(self):
        result = dict(type="image", image=self.raw["image"])
        if "alt" in self.raw:
            result["alt"] = self.raw["alt"]
        return result

    @property
    def code(self):
        result = dict(type="code", code=self.raw["code"], executable=True)
        return result

# Execute

import sys

if len(sys.argv) < 2:
    print("请输入需要导入的文件名, 记得使用绝对路径~")
    raise InfoError("Need argument: yaml file path")
yaml_path = sys.argv[1]

course = Course(yaml_path=yaml_path).load()
sql_host, sql_user, sql_pwd, sql_table, only_update = course.config
db = pymysql.connect(host=sql_host, user=sql_user, passwd=sql_pwd, db=sql_table, charset="utf8")
course.writeDB()
db.close()
exit()
